// File: thread.h
#ifndef  __THREAD_H__
#define  __THREAD_H__

//#define PTEST
/** 
 * @brief Velicina kontrolnog steka, max 64KB
 */
typedef unsigned long StackSize;
extern const StackSize defaultStackSize;

/** 
 * @brief Vreme u milisekundama
 */
typedef unsigned int Time; 
extern const Time defaultTimeSlice;

/** 
 * @brief ID unutar sistema, sistemski ID.
 */
typedef int ID;
/** 
 * @brief Korisnicki ID
 *
 * Ne mora biti isti kao i ID sistema. U ovoj implementaciji jeste.
 */
typedef int UID;

/* Za kasnije koriscenje na OS2 */
typedef unsigned int Priority;
extern Priority defaultPriority;

class PCB;

/** 
 * @brief Vraca najmanji vremenski interval izvrsavanja niti.
 * 
 * @return 
 * Vraca vreme koje je definisano sistemski.
 *
 * Ove vreme je za sada konstantno(55ms), ali u kasnijim implementacijama moze biti
 * uvecano zbog manjeg gubljenja vremena na promenu kontexta.
 */
Time minTimeSlice();

/** 
 * @brief Ova funkcija obezbedjuje promenu konteksta i preuzimanje od strane
 * sledece niti koja je na redu izvrsavanja
 *
 */
void dispatch();

/** 
 * @brief Klasa Thread definise korisnicki interfejs za rad sa nitima.
 */
class Thread {
    public:
        /** 
         * @brief Inicijalizuje stek niti i stavlja nit u scheduler na
         * izvrsavanje.
         */
        void start();
        /** 
         * @brief Blokira pozivajucu nit sve dok nit predstavljena objektom nad
         * kojim se ova operacija izvrsava nije zavrsena.
         *
         * Nit se nece blokirati ako pokusa da ceka samu sebe, ili ako je
         * upitanju jedna od dve sistemske niti. Takodje ne mozemo da cekamo na
         * nit koja je vec zavrsena.
         */
        void waitToComplete();
        /** 
         * @brief Nasilno budjenje niti iz blokiranog stanja
         *
         * Ako je nit blokirana na nekom semaforu ili dogadjaju pozivom ove
         * metode data nit ce biti odblokirana i stavljena u red spremnih.
         *
         * Ovaj poziv ce takodje izvrsiti promenu konteksta i preuzimanje...
         */
        void Interrupt();
        /** 
         * @brief Vraca ID niti
         * 
         */
        ID getId();
        /** 
         * @brief Vraca UID niti
         * 
         */
        UID getUID();
        /** 
         * @brief Dohvatanje niti na osnovu sistemskog ID
         * 
         * @param id
         * ID niti koja se trazi
         * 
         * @return 
         * Pokazivac na nit koja ima dati ID, ili NULL ako
         * takva nit ne postoji
         */
        static Thread * getThreadById(ID id);
        /** 
         * @brief Dohvatanje niti na osnovu korisnickog ID
         * 
         * @param id
         * UID niti koja se trazi
         * 
         * @return 
         * Funkcija vraca isti broj UID-a, posto je implementacija tako
         * odradjena.
         */
        static ID getIdOf(UID uid);
        /** 
         * @brief Brise nit iz sistema, ali pre toga joj dozvoljava da se
         * izvrsi do kraja.
         *
         * Ako ne bi na kraju pozvali destruktor, onda bi imali problema sa
         * konzistentnoscu podataka i bravama koje su zakljucane,...
         */
        virtual ~Thread();
    protected:
        friend class PCB;
        friend class Doxon;
        friend class Idle;
        /** 
         * @brief Formira sve podatke niti
         * 
         * @param uid 
         * Moze se zadati od strane korisnike, ako se stavi
         * vrednost -1, onda ce sistem sam izabrati jedinstveni broj.
         *
         * @param priority
         * Za sada nedefinisan...
         * Default vrednost je definisana sa konstantom defaultPriority(50)
         *
         * @param stackSize 
         * Velicina korisnickog steka niti.
         * Default vrednost je definisana sa konstantom defaultStackSize(4096B)
         *
         * @param timeSlice 
         * Vreme dodeljeno za izvrsavanje niti u sistemu.
         * Default vrednost je definisana sa konstantom defaultTimeSlice(100ms)
         */
        Thread (UID uid,
                Priority priority  = defaultPriority,
                StackSize stackSize= defaultStackSize,
                Time timeSlice     = defaultTimeSlice);
        /** 
         * @brief Kod niti se nalazi u ovoj metodi
         *
         * Svaka nova nit ovde stavlja svoj kod koji se izvrsava.
         */
        virtual void run() {}
    private:
        /** 
         * @brief Pokazivac na kernel implementaciju niti
         */
        PCB* myPCB;
};
#endif  /*__THREAD_H__*/
